#include <string.h>
#include <iostream>
#include <math.h>
#include "trainData.h"

///
/// \brief keyValue::keyValue
///

keyValue::keyValue()
{
    isNum = -1;
    valueInt = -1;
}

keyValue::keyValue(int in, char* k, int vi, char* vs)
{
    setValue(in, k, vi, vs);
}

int keyValue::setValue(int in, char *k, int vi, char *vs)
{
    isNum = in;
    valueInt = vi;
    strcpy(key, k);
    if(vs != NULL)
    {
        strcpy(valueStr, vs);

    }
    return 1;
}

///
/// \brief valueItem::valueItem
///

valueItem::valueItem()
{
    keyValueNum = 0;
    item = NULL;
    nextItem = NULL;
    addedKeyValueNum = 0;
}

int valueItem::setKeyValueNum(int n)
{
    keyValueNum = n;
    if(item != NULL)
    {
        delete item;
    }
    item = new keyValue[n];
    addedKeyValueNum = 0;
    return 1;
}

int valueItem::addKeyValue(keyValue *n)
{
    if(keyValueNum <= 0 || addedKeyValueNum >= keyValueNum)
    {
        return 0;
    }

    item[addedKeyValueNum] = *n;
    addedKeyValueNum ++;
    return 1;
}


int valueItem::setNextItem(valueItem *n)
{
    nextItem = n;
    return 1;
}

int valueItem::getKeyValueNum()
{
    return keyValueNum;
}

keyValue* valueItem::getItem()
{
    return item;
}

keyValue* valueItem::getKeyValue(int num)
{
    return &(item[num-1]);
}

keyValue* valueItem::getKeyValue(char *k)
{
    keyValue* tmp = NULL;
    for(int t = 0; t < keyValueNum; t ++)
    {
        tmp = item + t;
        if(strcmp(tmp->key, k) == 0)
        {
            return tmp;
        }
    }
    /*
    for(int t = 0; t <keyValueNum; t ++)
    {
        tmp = item + t;
        std::cout << t << " " << tmp->key << std::endl;
    }
    */
    //tmp = item+14;
    return NULL;
}


valueItem* valueItem::getNextItem()
{
    return nextItem;
}

///
/// \brief valueSelect::valueSelect
///

valueSelect::valueSelect()
{
    for(int t = 0; t < 30; t ++)
    {
        key[t] = '\0';
        value[t] = '\0';
    }
    valueNum = 0;
    classNum = 0;
    nextValue = NULL;
}


valueSelect::~valueSelect()
{
}

int valueSelect::clean()
{
    valueSelect* tmp, * deleteV;
    tmp = this->nextValue;
    while(tmp != NULL)
    {
        deleteV = tmp;
        tmp = tmp->nextValue;
        delete deleteV;
    }
    return 1;
}

valueSelect* valueSelect::isValueExist(char *v)
{
    valueSelect* tmp = nextValue;
    while(tmp != NULL)
    {
        if(strcmp(tmp->value, v) == 0)
        {
            return tmp;
        }
        tmp = tmp->nextValue;
    }
    return NULL;
}

int valueSelect::addValue(char *v)
{
    valueSelect* tmp = this->isValueExist(v);
    if(tmp == NULL)
    {
        tmp = new valueSelect;
        strcpy(tmp->value, v);
        tmp->classNum = 1;
        tmp->nextValue = this->nextValue;
        this->nextValue = tmp;
        valueNum ++;
        return 1;
    }
    else
    {
        tmp->valueNum ++;
        return 1;
    }
    return 1;
}

int valueSelect::setKey(char *k)
{
    strcpy(key, k);
    return 1;
}


///
/// \brief dataChosen::dataChosen
///

dataChosen::dataChosen()
{
    nextData = NULL;
    item = NULL;
    dataNum = 0;
}


int dataChosen::clean()
{
    dataChosen *tmp, *deleteD;
    tmp = this->nextData;
    while(tmp != NULL)
    {
        deleteD = tmp;
        tmp = tmp->nextData;
        delete deleteD;
    }
    return 1;
}


dataChosen::dataChosen(trainData *d)
{
    nextData = NULL;
    item = NULL;
    dataNum = 0;

    valueItem* tmp = d->itemLib;
    while(tmp != NULL)
    {
        this->addItem(tmp);
        //dataNum ++;
        tmp = tmp->getNextItem();
    }
}

int dataChosen::addItem(valueItem *n)
{
    dataChosen* tmp = new dataChosen;
    tmp->item = n;
    tmp->nextData = this->nextData;
    this->nextData = tmp;
    dataNum ++;
    return 1;
}


valueSelect* dataChosen::getValueClass(char *k)
{
    valueSelect* result = new valueSelect;
    result->setKey(k);
    dataChosen* tmp = this->nextData;
    while(tmp != NULL)
    {
        //keyValue* tmpKV = ((tmp->item)->getKeyValue(k));
        result->addValue(((tmp->item)->getKeyValue(k))->valueStr);
        tmp = tmp->nextData;
    }
    return result;

}


dataChosen* dataChosen::getKeyValueItem(char *k, char *v)
{
    dataChosen* result = new dataChosen;

    dataChosen* tmp = this->nextData;
    while(tmp != NULL)
    {
        if(strcmp(tmp->item->getKeyValue(k)->valueStr, v) == 0)
        {
            result->addItem(tmp->item);
        }
        tmp = tmp->nextData;
    }
    return result;

}

double dataChosen::info()
{
    valueSelect* resultClass = this->getValueClass((char*)"class");
    double result = 0;
    double pi = 0;
    valueSelect* tmpVS = resultClass->nextValue;
    while(tmpVS != NULL)
    {
        pi = tmpVS->classNum / (this->dataNum+0.0);
        result += pi * (log10(pi) / log10(2));
        tmpVS = tmpVS->nextValue;
    }
    //std::cout << "info" << std::endl;
    resultClass->clean();
    delete resultClass;
    return result * (-1);
}


double dataChosen::infoKey(char *k)
{
    if(this->nextData->item->getKeyValue(k)->isNum == 1)
    {
        return infoKeyContin(k);
    }

    double result = 0;
    valueSelect* tmpVS = this->getValueClass(k);
    valueSelect* deleteTmpVS = tmpVS;
    dataChosen* tmpDC = NULL;
    tmpVS = tmpVS->nextValue;
    while(tmpVS != NULL)
    {
        tmpDC = this->getKeyValueItem(k, tmpVS->value);
        result += tmpVS->classNum / (this->dataNum+0.0) * tmpDC->info();
        tmpDC->clean();
        delete tmpDC;
        tmpVS = tmpVS->nextValue;
    }
    deleteTmpVS->clean();
    delete deleteTmpVS;
    return result;
}

double dataChosen::gain(char *k)
{
    return this->info() - this->infoKey(k);
}


int dataChosen::quickSort(int *l, int begin, int end)
{
    if(begin >= end)
    {
        return 1;
    }
    int cache = l[begin];
    int i = begin;
    int j = end;

    while(i < j)
    {
        while(i < j && l[j] >= cache)
            j --;
        if(i < j && l[j] < cache)
            l[i] = l[j];
        while(i < j && l[i] <= cache)
            i ++;
        if(i < j && l[i] > cache)
            l[j] = l[i];
    }

    l[i] = cache;
    quickSort(l, begin, i - 1);
    quickSort(l, i + 1, end);
    return 1;
}


double dataChosen::infoKeyContin(char *k, int getV)
{
    int* l = new int[this->dataNum];
    dataChosen* tmpDC = this->nextData;
    for(int t = 0; tmpDC != NULL; t ++, tmpDC = tmpDC->nextData)
    {
        l[t] = tmpDC->item->getKeyValue(k)->valueInt;
    }
    this->quickSort(l, 0, this->dataNum - 1);

    double getInfo = 0;
    double getValue = 0;
    dataChosen* tmpData = NULL;
    /*
    if(strcmp(k, "fnlwgt") == 0)
    {
        getValue = (l[0] + l[dataNum-1])/2.0;
        tmpData = this->getKeyContin(k, getValue, -1);
        getInfo += (tmpData->dataNum / (this->dataNum+0.0)) * tmpData->info();
        if(tmpData != NULL)
        {
            tmpData->clean();
            delete tmpData;
        }
        tmpData = this->getKeyContin(k, getValue, 1);
        getInfo += (tmpData->dataNum / (this->dataNum+0.0)) * tmpData->info();
        if(tmpData != NULL)
        {
            tmpData->clean();
            delete tmpData;
        }
        delete [] l;

        if(getV == 1)
            return getValue;

        return getInfo;

    }
    */

    double tmpInfo = 0;
    double tmpValue = 0;
    double beyondNum;

    int tmp = 0;
    tmpValue = (l[tmp] + l[tmp+1])/2.0;
    beyondNum = tmpValue;
    tmpData = this->getKeyContin(k, tmpValue, -1);
    tmpInfo += (tmpData->dataNum / (this->dataNum+0.0)) * tmpData->info();
    if(tmpData != NULL)
    {
        tmpData->clean();
        delete tmpData;
    }
    tmpData = this->getKeyContin(k, tmpValue, 1);
    tmpInfo += (tmpData->dataNum / (this->dataNum+0.0)) * tmpData->info();
    if(tmpData != NULL)
    {
        tmpData->clean();
        delete tmpData;
    }

    getInfo = tmpInfo;
    getValue = tmpValue;

    tmpInfo = 0;

    tmp ++;
    //int count = 0;
    while(tmp < dataNum)
    {
        //count ++;
        //std::cout << count << std::endl;
        tmpInfo = 0;
        tmpValue = (l[tmp] + l[tmp+1])/2.0;
        if(tmpValue == beyondNum)
        {
            tmp ++;
            continue;
        }
        beyondNum = tmpValue;

        tmpData = this->getKeyContin(k, tmpValue, -1);
        tmpInfo += (tmpData->dataNum / (this->dataNum+0.0)) * tmpData->info();
        if(tmpData != NULL)
        {
            tmpData->clean();
            delete tmpData;
        }
        tmpData = this->getKeyContin(k, tmpValue, 1);
        tmpInfo += (tmpData->dataNum / (this->dataNum+0.0)) * tmpData->info();
        if(tmpData != NULL)
        {
            tmpData->clean();
            delete tmpData;
        }
        if(tmpInfo < getInfo)
        {
            getInfo = tmpInfo;
            getValue = tmpValue;
        }
        tmp ++;
    }

    delete [] l;

    if(getV == 1)
        return getValue;

    return getInfo;

}

dataChosen* dataChosen::getKeyContin(char *k, double divi, int sign)
{
    dataChosen* result = new dataChosen;
    dataChosen* tmpData = this->nextData;
    while(tmpData != NULL)
    {
        switch (sign) {
        case -1:
            if(tmpData->item->getKeyValue(k)->valueInt <= divi)
            {
                result->addItem(tmpData->item);
            }

            break;
        case 1:
            if(tmpData->item->getKeyValue(k)->valueInt > divi)
            {
                result->addItem(tmpData->item);
            }
            break;
        }
        tmpData = tmpData->nextData;
    }
    return result;
}


///
/// \brief trainData::trainData
///

trainData::trainData()
{
    itemNum = 0;
    itemLib = itemLibTail = NULL;

}

int trainData::addItem(valueItem n)
{
    valueItem* newItem = new valueItem;
    *newItem = n;
    newItem->setNextItem(NULL);

    if(itemNum == 0)
    {
        itemLib = newItem;
        itemLibTail = newItem;
    }
    else
    {
        itemLibTail->setNextItem(newItem);
        itemLibTail = newItem;
    }
    itemNum ++;
    return 1;
}

int trainData::showData()
{
    //std::cout << "here" << std::endl;
    valueItem* tmpItem = itemLib;
    keyValue* tmpKeyValue = NULL;
    int tmpItemNum = 0;
    while(tmpItem != NULL)
    {
        /*
        if(strcmp((tmpItem->getItem() + 14)->key, "class") == 0)
        {

            tmpItem = tmpItem->getNextItem();
            tmpItemNum ++;
            continue;

        }
        */

        std::cout << tmpItemNum << " ";
        for(int t = 1; t <= tmpItem->getKeyValueNum(); t ++)
        {
            tmpKeyValue = tmpItem->getKeyValue(t);
            std::cout << tmpKeyValue->key << ":";
            if(tmpKeyValue->isNum == 1)
            {
                std::cout << tmpKeyValue->valueInt << " ";
            }
            else
            {
                std::cout << tmpKeyValue->valueStr << " ";
            }
        }
        std::cout << std::endl;
        tmpItem = tmpItem->getNextItem();
        tmpItemNum ++;
    }

    std::cout << std::endl << "item Num: " << tmpItemNum << std::endl;
    return 1;

}
